#pragma once

#include <iostream>
#include <string>
#include <string.h>
#include <mutex>
#include <vector>
#include <fstream>
#include <cstring>
#include <assert.h>
#include <algorithm>
#include <jsoncpp/json/json.h>

#include "../comm/httplib.h"
#include "../comm/util.hpp"
#include "../comm/log.hpp"
#include "oj_view.hpp"

// #include "oj_model.hpp"
#include "oj_model2.hpp"

static const std::string service_machine = "./conf/service_machine.conf";

class Machine
{
public:
    std::string ip;  // 提供编译服务的服务器ip
    int port;        // 提供编译服务的服务器port
    uint64_t load;   // 提供编译服务的服务器负载  //! ??
    std::mutex *mtx; // mutex 是禁止拷贝的,使用指针

    Machine() : ip(""), port(0), load(0), mtx(nullptr) {}
    ~Machine() {}

    // 提升主机负载
    void IncLoad()
    {
        if (mtx)
            mtx->lock();
        ++load;
        if (mtx)
            mtx->unlock();
    }

    // 减少主机负载
    void DecLoad()
    {
        if (mtx)
            mtx->lock();
        --load;
        if (mtx)
            mtx->unlock();
    }

    uint64_t GetLoad()
    {
        uint64_t num = 0;
        if (mtx)
            mtx->lock();
        num = load;
        if (mtx)
            mtx->unlock();
        return num;
    }

    // 将负载清空
    void ResetLoad()
    {
        if (mtx)
            mtx->lock();
        load = 0;
        if (mtx)
            mtx->unlock();
    }
};

// 负载均衡模块
class LoadBlance
{
    // 可以给我们提供编译服务的所有主机列表
    // 每一台主机都有自己对应的下标,充当 当前主机的ip
    std::vector<Machine> machines;
    // 在线的主机id
    std::vector<int> online;
    // 离线的主机id
    std::vector<int> offline;

    // 在选择 主机时保证LoadBlance 的安全
    std::mutex mtx;

public:
    LoadBlance()
    {
       
        // 加载所有的主机
        assert(LoadConf(service_machine));
        ns_log::lg.LogMessage(ns_log::Info, "加载 %s 成功\n", service_machine.c_str());
        std::cout<<"tttttttttssss"<<std::endl;
    }
    ~LoadBlance() {}

    bool LoadConf(const std::string &machine_conf)
    {
        std::ifstream in(machine_conf);
        if (!in.is_open())
        {
            ns_log::lg.LogMessage(ns_log::Fatal, "加载 %s 失败\n", machine_conf.c_str());
            return false;
        }
        std::string line;
        while (std::getline(in, line))
        {
            std::vector<std::string> tokens;
            ns_util::StringUtil::SplitString(line, &tokens, ":");
            if (tokens.size() != 2)
            {
                // for(auto e:tokens)
                // std::cout<<e<<" ";
                // std::cout<<std::endl;
                ns_log::lg.LogMessage(ns_log::Warning, "LoadBlance 加载主机失败\n");
            }
            Machine m;
            m.ip = tokens[0];
            m.port = std::stoi(tokens[1]);
            m.mtx = new std::mutex();

            online.push_back(machines.size()); // 插入下标
            machines.push_back(m);
        }

        in.close();
        return true;
    }

    //
    bool SmartChoice(int *id, Machine **m)
    {
        // 1.使用选择负载最小的主机
        mtx.lock();

        // 负载均衡算法
        // 1.随机数 + Hash
        // 2.轮询 + Hash

        (*id) = online[0];
        (*m) = &machines[online[0]]; // Machine 中存在 mutex ,不能拷贝对象,要拷贝值

        int online_num = online.size();
        if (online_num == 0) // 后台没有在线的服务器
        {
            mtx.unlock();
            ns_log::lg.LogMessage(ns_log::Fatal, "Bad Error!,SmartChoice ");
            return false;
        }

        // 获取对应主机
        uint64_t min_load = machines[online[0]].GetLoad();
        for (int i = 1; i < online_num; i++)
        {
            uint64_t current_load = machines[online[i]].GetLoad();
            if (min_load > current_load)
            {
                min_load = current_load;
                *id = online[i];
                *m = &machines[online[i]];
            }
        }

        mtx.unlock();

        return true;
    }

    // 离线
    void OfflineMachine(int id)
    {
        mtx.lock();

        for (auto e = online.begin(); e != online.end(); e++)
        {
            if (*e == id)
            {
                // 离线目标主机
                machines[id].ResetLoad(); // 对目标主机负载清零
                offline.push_back(*e);
                online.erase(e);
                break;
            }
        }

        mtx.unlock();
    }

    // 在线
    void OnlineMachine()
    {
        // 当所有主机离线的时候,同一对其上线
        mtx.lock();
        // 在online上插入 offline 的列表,对其全部上线
        online.insert(online.end(), offline.begin(), offline.end());
        // 对离线主机中已经上限的列表进行删除
        offline.erase(offline.begin(), offline.end());
        mtx.unlock();

        ns_log::lg.LogMessage(ns_log::Info, "离线主机已经重新上线\n");
    }

    void For_Test_Dubug()
    {
        std::cout << " 在线主机列表: " << std::endl;
        for (auto e : online)
        {
            std::cout << e << "  ";
        }
        std::cout << std::endl;
        std::cout << " 离线主机列表: " << std::endl;
        for (auto e : offline)
        {
            std::cout << e << "  ";
        }
        std::cout << std::endl;
    }
};

namespace ns_control
{

    // 核心业务逻辑控制器
    class Control
    {

        ns_view::View view; // 提供html 渲染功能
        ns_model::Model model;  // 提供后台数据
        LoadBlance load_blance; // Control 核心负载均衡器
        
    public:
        Control() {
           
        }
        ~Control() {}

        // 恢复离线主机
        void RecoveryMachines()
        {
            load_blance.OnlineMachine();
        }

        bool AllQuestions_ToJson(std::string &json)
        {
            std::vector<ns_model::Question> All_Question;
            if (model.GetAllQuestionList(&All_Question))
            {
                std::sort(All_Question.begin(), All_Question.end(), [](const ns_model::Question &q1, const ns_model::Question &q2) -> bool
                          { return std::stoi(q1.number) < std::stoi(q2.number); });

                Json::Value root;
                root["code"] = 0;
                root["msg"] = "";
                root["count"] = All_Question.size();

                /*

                q.number = row[0];
                q.title = row[1];
                q.star = row[2];
                q.desc = row[3];
                q.header = row[4];
                q.tail = row[5];
                q.cpu_limit = atoi(row[6]);
                q.mem_limit = atoi(row[7]);

                 */
                for (auto &e : All_Question)
                {
                    Json::Value r1;
                    r1["id"] = e.number;
                    r1["title"] = e.title;
                    r1["star"] = e.star;
                    r1["time_limit"] = e.cpu_limit;
                    r1["mem_limit"] = e.mem_limit;

                    root["data"].append(r1);
                }

                Json::FastWriter style;
                json = style.write(root);

                return true;
            }
            return false;
        }

        // 根据题目数据构建网页
        bool AllQuestions(std::string *html)
        {
            bool result = true;
            std::vector<ns_model::Question> All_Question;
            // if (model.GetAllQuestionList(&All_Question))
            // {
            //     std::sort(All_Question.begin(), All_Question.end(), [](const ns_model::Question &q1, const ns_model::Question &q2) -> bool
            //               { return std::stoi(q1.number) < std::stoi(q2.number); });
            // 获取题目信息成功,将所有的题目数据构建成网页
            view.AllExpandHtml(All_Question, html);
            // }
            // else
            // {
            // result = false;
            //     *html = "获取题目失败,形成题目列表失败";
            // }
            return result;
        }

        bool AllQuestions_table(std::string *html)
        {
            bool result = true;
            std::vector<ns_model::Question> All_Question;
            if (model.GetAllQuestionList(&All_Question))
            {
                std::sort(All_Question.begin(), All_Question.end(), [](const ns_model::Question &q1, const ns_model::Question &q2) -> bool
                          { return std::stoi(q1.number) < std::stoi(q2.number); });
                // 获取题目信息成功,将所有的题目数据构建成网页
                view.AllExpandHtml_1(All_Question, html);
            }
            else
            {
                result = false;
                *html = "获取题目失败,形成题目列表失败";
            }
            return result;
        }

        // 获取指定一题
        bool OneQuestion(const std::string &number, std::string *html)
        {
            bool result = true;
            ns_model::Question q;
            if (model.GetOneQuestion(number, &q))
            {
                view.OneExpandHtml(q, html);
            }
            else
            {
                result = false;
                *html = "题目: " + number + " 获取失败";
            }
            return result;
        }

        // 判题接口
        void Judge(const std::string &number, const std::string &in_json, std::string *out_json)
        {
            //  id : 题目的id
            // code: 代码正文

            // 先对 in_json 进行反序列化,得到题目id,得到用户提交的代码
            // 重新拼接用户代码 + 测试用例代码, 形成新的代码
            // 选择负载最低的主机
            // 发起 http 请求,得到结果
            // 将结果赋值给 out_json

            //? 根据特定的题号,获取对应的题目细节

            ns_model::Question q;
            model.GetOneQuestion(number, &q);

            // 1. in_json 进行反序列化,得到题目id,得到用户提交的源代码
            Json::Reader reader;
            Json::Value in_value;
            reader.parse(in_json, in_value);

            // 用户提交的代码
            std::string code = in_value["code"].asString();

            // 2. 重新拼接用户的代码+测试用例代码,形成新的代码
            // ^ 重新构造新的 Json ,再发送给服务器进行运行
            Json::Value compile_value;
            compile_value["input"] = in_value["input"].asString();

            // 用户代码 + 题目中的 tail (测试用例)

            compile_value["code"] = code + "\n" + q.tail; //! 必须将用户代码和 测试用例做拼接,否则无法运行

            compile_value["cpu_limit"] = q.cpu_limit;
            compile_value["mem_limit"] = q.mem_limit;

            Json::FastWriter writer;
            std::string comlile_string = writer.write(compile_value);

            // 增加提交次数
            model.Add_Try_count(number);

            // 3. 选择负载最低的主机(差错处理)
            // 规则:一直选择,直到主机可用,否则全部挂掉

            while (true)
            {
                int id = 0;
                Machine *m = nullptr;
                if (!load_blance.SmartChoice(&id, &m))
                {
                    // 选择失败
                    // 说明所有的主机都 挂掉了
                    break;
                }
                ns_log::lg.LogMessage(ns_log::Info, "选择主机成功,主机id: %d 详情: %s:%d \n", id, m->ip.c_str(), m->port);

                httplib::Client cli(m->ip, m->port);
                m->IncLoad(); // 增加负载
                //  cli.Post 返回的是一个 req

                if (auto res = cli.Post("/compile_and_run", comlile_string, "application/json;charset=utf-8"))
                {

                    // 判断 Http 请求的状态码是否为 200 ,只有状态码为 200 才是请求成功的
                    if (res->status == 200)
                    {
                        // 将结果赋值给 *out_json
                        *out_json = res->body;
                        m->DecLoad(); // 减少负载

                        Json::Value _json;
                        Json::Reader reader;
                        bool res = reader.parse(*out_json, _json);

                        // 成功 AC 数++
                        if (_json["status"].asInt() == 0)
                            model.Add_Success_count(number);

                        ns_log::lg.LogMessage(ns_log::Info, "请求和运行服务成功...\n");
                        break;
                    }
                    else
                    {
                        // 出错

                        //// 离线该主机,同时 对其 load --
                        m->DecLoad();
                        // 这里什么也不做, 会退出循环,然后重新选择 新的主机
                    }
                }
                else
                {
                    // 请求失败
                    ns_log::lg.LogMessage(ns_log::Warning, "当前请求的主机id: %d 详情: %s:%d 可能已经离线", id, m->ip.c_str(), m->port);

                    m->DecLoad(); // 负载 --

                    // 请求失败,将失败的主机进行离线
                    load_blance.OfflineMachine(id);
                    load_blance.For_Test_Dubug();
                }
            }

            //
        }
    };
}